package com.ysjy.course.redis;

import com.wkbb.common.constants.Constants;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedissonUtil {

    /**强制解锁时间设置*/
    private long LOCK_TIME = 2000L;

    /**等待时间**/
    private long WAIT_TIME = 1000L;

    /**休眠时间**/
    private long SLEEP_TIME = 200L;

    private RedissonConfig redissonConfig;

    private volatile static Redisson redisson = null;

    @Autowired
    public void init(RedissonConfig redissonConfig) {
        this.redissonConfig = redissonConfig;
    }

    public Redisson getRedissonClient(){
        if(redisson == null) {
            synchronized (Redisson.class) {
                if (redisson == null) {
                    String redisUrl = "redis://" + redissonConfig.getHost() + ":" + redissonConfig.getPort();
                    String pass = null;
                    if(!StringUtils.isEmpty(redissonConfig.getPassword())){
                        pass = redissonConfig.getPassword();
                    }

                    Config config = new Config();
                    config.useSingleServer()
                    .setAddress(redisUrl)
                    .setPassword(pass)
                    .setConnectionMinimumIdleSize(Constants.INIT_MAP_SIZE_5)
                    //同任何节点建立连接时的等待超时。时间单位是毫秒。默认：10000
                    .setConnectTimeout(30000)
                    //当与某个节点的连接断开时，等待与其重新建立连接的时间间隔。时间单位是毫秒。默认:3000
                    .setReconnectionTimeout(10000)
                    //等待节点回复命令的时间。该时间从命令发送成功时开始计时。默认:3000
                    .setTimeout(10000)
                    //如果尝试达到 retryAttempts（命令失败重试次数） 仍然不能将命令发送至某个指定的节点时，将抛出错误。如果尝试在此限制之内发送成功，则开始启用 timeout（命令等待超时） 计时。默认值：3
                    .setRetryAttempts(5)
                    //在一条命令发送失败以后，等待重试发送的时间间隔。时间单位是毫秒。     默认值：1500
                    .setRetryInterval(3000);

                    redisson = (Redisson)Redisson.create(config);
                }
            }
        }
        return redisson;
    }

    /**
     * 是否锁定
     * */
    public boolean isLocked(String lockName){
        return getRedissonClient().getLock(lockName).isLocked();
    }

    /**
     *  根据key值获取锁
     * @param lockName
     */
    public void lock(String lockName){
        RLock lock = getRedissonClient().getLock(lockName);
        //lock提供带timeout参数，timeout结束强制解锁，防止死锁
        lock.lock(LOCK_TIME, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取锁
     * @param lockName
     * @return
     * @throws InterruptedException
     */
    public boolean tryLock(String lockName) throws InterruptedException {
        RLock lock = getRedissonClient().getLock(lockName);
        //tryLock，第一个参数是等待时间。 第二个参数 强制锁释放时间
        return lock.tryLock(WAIT_TIME,LOCK_TIME,TimeUnit.MILLISECONDS);
    }

    /**
     * 解锁
     * @param lockName
     */
    public void unLock(String lockName){
        RLock lock = getRedissonClient().getLock(lockName);
        lock.unlock();
    }

    /**
     * 获取锁，一直等待到取到锁后返回
     * @param lockName
     * @throws InterruptedException
     */
    public boolean getUntilHaveLock(String lockName) throws InterruptedException {
        while (true){
            if(tryLock(lockName)){
                return true;
            }else{
                Thread.sleep(SLEEP_TIME);
            }
        }
    }

}
